基本用法
AsyncTask是Android种常用的一种轻量级异步类,它的基本使用如下:
1 | class SomeTask extends AsyncTask<Integer, Integer, String>{ |
AsycTask种有四个基本的回调方法,它们分别是
- onPreExecute 在任务执行前在UI线程回调
- doInBackground 在后台线程种执行任务
- onProgressUpdate 将任务的执行进度通过onProgreeUpdate通知给UI线程
- onPostExecute 将任务执行的结果回调给UI线程
源码分析
为了清楚的了解AsyncTask内部的原理机制,本篇将会对其源码进行解析,在这之前我们先看看它的类结构定义
AsyncTask的类结构
1 | public abstract class AsyncTask<Params, Progress, Result> { |
AsyncTask是个抽象的泛型类,它有三个抽象的参数<Params, Progress, Result>,分别代表着后台任务附带的参数Params,执行任务的进度Progress以及执行执行的结果Result。它的类成员主要如下:
- THREAD_POOL_EXECUTOR 这是AsyncTask执行异步任务的线程池,线程池的基本大小为CPU_COUNT + 1,最大大小为CPU_COUNT * 2 + 1,并使用一个有界大小为128的LinkedBlockingQueue作为其工作队列,线程保活时间为1s。
- sDefaultExecutor 它是一个SerialExecutor,这个是用来对提交的任务进行管理的的执行器,任务最终通过它提交给线程池,后面再详细介绍。
- sHandler 它是一个InternalHandler,负责投递执行进度和最终结果给UI线程的Handler。
- mWorker 这是一个WorkerRunnable类型的Callable,负责执行AsyncTask的后台任务。
- mFuture FutureTask可以看作是mWorker的包裹,负责处理一些任务执行完的收尾工作,它是最终提交给SerialExecutor的任务。
- mStatus AsyncTask当前的状态,默认是Status.PENDING状态表示等待任务中。
需要注意的是除了mStatus,以上所有的成员均为static final类型,也就是说这些成员是给AsyncTask类使用的。
了解了类成员再看看它的构造方法
1 | public AsyncTask() { |
AsyncTask只有这一个默认构造方法,在其内部会构造一个WorkerRunnable代表着我们的后台任务,WorkerRunnable仅仅是继承了Callable同时内部有一个Params数组代表着后台任务附带的参数,以及一个最终提交给任务调度器的FutureTask,它实际上可以看作是mWorker的包裹,内部保存了mWroker这个callable。
任务提交
AsyncTask一般通过execute方法来提交我们的异步任务,在提交时会传递参数Params给execute方法,我们看看它的实现
1 | public final AsyncTask<Params, Progress, Result> execute(Params... params) { |
在execute方法中通过executeOnExecutor实现,在它内部首先检测当前mStatus的状态,如果已经执行或者完成那么抛出异常,这说明AsyncTask是只能够被执行一次的,否则置mStatus为Status.RUNNING表示正在执行状态。然后回调onPreExecute,这个是任务执行前的回调。随后将参数保存在mWorker中,然后通过将后台任务提交给sDefaultExecutor即任务调度器它是个SerialExecutor,我们看看它的实现。
任务调度器SerialExecutor
1 | private static class SerialExecutor implements Executor { |
SerialExecutor内部实现很简单,它有一个ArrayDeque
1 | public void run() { |
这段代码将在线程中执行,这里的r实际就是我们提交的mFuture,这时候执行它的run方法,执行完成后调用scheduleNext方法开始下一个任务的执行。可见SerialExecutor是一个串行的任务调度器。它在一个任务执行完成后才开始调用下个任务执行。
任务的执行过程
在上面我们知道了SerialExecutor通过scheduleNext从任务队列中取出一个任务提交给线程池开始执行,而提交的任务内部最终会去执行FutureTask的run方法,而run方法中最终会执行它内部的callable,也就是mWorker,还记得么,我们在构造方法中创建的它,实现是这样的:
1 | mWorker = new WorkerRunnable<Params, Result>() { |
在call回调中执行doInBackground方法执行后台任务,并将执行的结果通过postResult发送给UI线程,这里都是在线程池中的线程中执行的。
结果的投递
任务执行的结果是在线程中通过postResult方法投递给UI线程的,我们看看它的实现:
1 | private Result postResult(Result result) { |
通过sHandler处理投递的MESSAG_POST_RESULT消息,投递的结果被封装在了AsyncTaskResult类型的对象中。我们看下这个类
1 | "RawUseOfParameterizedType"}) ({ |
在AsyncTaskResult内部保存了AsyncTask的实例mTask以及投递的结果mData。
接下来就是通过sHandler也就是InternalHandler来处理消息,我们看看InternalHandler的实现:
1 | private static class InternalHandler extends Handler { |
在InternalHandler内部只处理了两类消息,MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS,当我们在doBackground方法中执行后台任务时,可以通过publishProgress方法来通知UI线程我们当前任务的进度,在publishProgress方法中会通过InternalHandler来发送MESSAGE_POST_PROGRESS消息,InternalHandler接收到消息后通过onProgressUpdate回调告知UI层,而MESSAGE_POST_RESULT通过finish来处理。我们先看看finish方法的具体实现
1 | private void finish(Result result) { |
在finish方法中首先判断任务是否被取消,如果已经取消回调onCancelled处理,否则执行onPostExecute回调将结果投递给UI线程,最后修改mStatus的状态为Status.FINISHED表示任务已经执行完成。
总结
从AsyncTask的实现来看,它本质上和Thread+Handler的方式类似,内部对于任务的处理默认是串行进行处理的(当然如果通过executeOnExecutor也是可以进行并行处理的)。AsyncTask最大的又是也许就是封装的几个线程回调使得在Android执行后台任务时使用起来更加方便整体来看是一个完整可控的过程。但由于AsyncTask的设计涉及到UI线程,所以太耗时的任务并不太适用于AsyncTask,况且在串行的情况下提交的任务可能要经过很长时间才能被处理完成。它内部的线程池决定了它适合同类型的任务,而对不同类型的任务处理第一可能会降低线程池的效率,第二可能会增加代码复杂性。另一个方面AsyncTask的使用不当会带来令人诟病的内存泄漏问题,因为AsyncTask和UI组件的生命周期并不同步,比如非静态的AsyncTask会持有Activity的引用,当屏幕旋转时AsyncTask可能会去更新一个已经销毁掉的Activity,这些都要求我们在使用AsyncTask时应该更加谨慎,一般是采用静态的AsyncTask结合弱引用的方式,并且在UI组件生命期结束的时候通过cancel及时的取消任务,同时在AsyncTask内部我们也可以根据取消状态来做相应的处理。